﻿using CleanArchitecture.Core.Handlers;
using CleanArchitecture.Core.Interfaces.Cache;
using CleanArchitecture.Infrastructure.AutoMapper;
using CleanArchitecture.Infrastructure.Configuration;
using CleanArchitecture.Infrastructure.Entities;
using CleanArchitecture.Infrastructure.Entities.AdminUsers;
using CleanArchitecture.Infrastructure.Entities.Business;
using CleanArchitecture.Infrastructure.Entities.SystemModules;
using CleanArchitecture.Infrastructure.Interfaces;
using CleanArchitecture.Infrastructure.Interfaces.Business;
using CleanArchitecture.Infrastructure.Interfaces.Security;
using CleanArchitecture.Infrastructure.Interfaces.SystemManage;
using CleanArchitecture.Infrastructure.Models.Business.Columns;
using CleanArchitecture.Infrastructure.Models.SystemManage;
using System;
using System.Collections.Generic;
using System.Linq;

namespace CleanArchitecture.Infrastructure.Services.Security
{
    /// <summary>
    /// DavidLee 2020/01/04 重新构架分类权限
    /// 所有系统模块均通过数据字典模块分配操作按钮，再通过角色分配按钮操作权限
    /// </summary>
    public partial class PermissionService : IPermissionService
    {
        #region Fields
        private readonly ISystemModuleService _systemModuleService;
        private readonly ISystemDataDicService _systemdatadicService;
        private readonly IColumnService _columnService;
        private readonly IStaticCacheManager _cacheManager;
        private readonly IWorkContext _workContext;
        private readonly IRepository<Column> _columnRepository;
        private readonly IRepository<SystemModule> _systemmoduleRepository;
        private readonly IRepository<AdminRoleBusColumnMapping> _adminrolecolumnMappingRepository;
        private readonly IRepository<AdminRoleSystemModuleMapping> _adminrolesysmoduleMappingRepo;
        #endregion

        #region Ctor

        public PermissionService(ISystemModuleService systemModuleService,
            ISystemDataDicService systemdatadicService,
            IColumnService columnService,
            IStaticCacheManager catachManager,
            IWorkContext workContext,
            IRepository<Column> columnRepo,
            IRepository<SystemModule> systemmoduleRepository,
            IRepository<AdminRoleBusColumnMapping> adminrolecolumnMappingRepo,
            IRepository<AdminRoleSystemModuleMapping> adminrolesysmoduleMapping)
        {
            _systemModuleService = systemModuleService;
            _systemdatadicService = systemdatadicService;
            _columnService = columnService;
            _cacheManager = catachManager;
            _workContext = workContext;
            _columnRepository = columnRepo;
            _systemmoduleRepository = systemmoduleRepository;
            _adminrolesysmoduleMappingRepo = adminrolesysmoduleMapping;
            _adminrolecolumnMappingRepository = adminrolecolumnMappingRepo;
        }

        #endregion

        #region Methods
        /// <summary>
        /// Authorize permission
        /// 通过导航模块在系统名称获取模块是否授权用户角色，防止通过链接访问后端功能
        /// 需要满足以下条件：
        /// 1：系统模块为导航菜单
        /// 2：系统模块为系统用户角色分配使用权限
        /// </summary>
        /// <param name="moduleUri">moduleUri Route</param>
        /// <returns>true - authorized; otherwise, false</returns>
        public virtual bool Authorize(SystemModule module)
        {
            if (module == null)
                return false;
            var CurrentUser = _workContext.CurrentAdminUser;
            if (CurrentUser == null) return false;
            if (module.ModuleType != EntityEnumConfig.SysModuleType.ModulePage) return false;
            //Cache adminuserid, moduleid
            string cacheKey = string.Format(DefaultCacheConfig.AdminUserPermissionRecord, CurrentUser.Id, module.Id);
            return _cacheManager.Get(cacheKey, () => {
                var userRoles = CurrentUser.GetAdminRoles();
                //超级管理员拥有系统模块所有权限
                if (_workContext.IsAdmin)
                    return true;
                bool permission = false;
                var RolePermissions = _systemModuleService.GetAdminRoleSystemModuleMappingsByRoleIds(userRoles.Select(r => r.Id).ToArray());
                if (RolePermissions.Any(mp => mp.FKSystemModuleId.Equals(module.Id) && mp.IsAllow == EntityEnumConfig.YesorNo.YES))
                    permission = true;
                return permission;
            });
        }

        public virtual bool AuthorizeModuleButton(int moduleId, int buttonId)
        {
            //Cache adminuserid, moduleid, buttonid
            var CurrentUser = _workContext.CurrentAdminUser;
            string cacheKey = string.Format(DefaultCacheConfig.AdminUserPermissionButton, CurrentUser.Id, moduleId, buttonId);
            return _cacheManager.Get(cacheKey, () => {
                var userRoles = CurrentUser.GetAdminRoles();
                if (userRoles.Any(r => r.IsAdmin == EntityEnumConfig.YesorNo.YES))
                    return true;
                bool permission = false;
                var RolePermissions = _systemModuleService.GetAdminRoleSystemModuleMappingsByRoleIds(userRoles.Select(r => r.Id).ToArray());
                foreach (var mp in RolePermissions.Where(m => m.FKSystemModuleId == moduleId).ToList())
                {
                    if (!string.IsNullOrEmpty(mp.AuthorizedButtonIds))
                    {
                        //DavidLee 2020/10/14修复按钮使用IndexOf判断是否有操作权限的错误
                        //if (mp.AuthorizedButtonIds.IndexOf(buttonId.ToString()) >= 0)
                        if (mp.AuthorizedButtonIds.Split(',').Any(authid => authid.Equals(buttonId.ToString())))
                        {
                            permission = true;
                            break;
                        }
                    }
                }
                return permission;
            });
        }

        /// <summary>
        /// Column Authorize permission
        /// 针对栏目菜单基于用户角色的权限控制
        /// </summary>
        /// <param name="moduleUri">moduleUri Route</param>
        /// <returns>true - authorized; otherwise, false</returns>
        public virtual bool ColumnAuthorize(int ColumnId)
        {
            if (ColumnId <= 0)
                return false;
            var CurrentUser = _workContext.CurrentAdminUser;
            if (CurrentUser == null) return false;
            //Cache adminuserid, columnId
            string cacheKey = string.Format(DefaultCacheConfig.AdminUserColumnPermissionRecord, CurrentUser.Id, ColumnId);
            return _cacheManager.Get(cacheKey, () => {
                var userRoles = CurrentUser.GetAdminRoles();
                //超级管理员拥有系统模块所有权限
                if (_workContext.IsAdmin)
                    return true;
                bool permission = false;
                var RolePermissions = _columnService.GetAdminRoleColumnMappingsByRoleIds(userRoles.Select(r => r.Id).ToArray());
                if (RolePermissions.Any(mp => mp.FKColumnId.Equals(ColumnId) && mp.IsAllow == EntityEnumConfig.YesorNo.YES))
                    permission = true;
                return permission;
            });
        }

        public virtual bool AuthorizeColumnButton(int columnId, int buttonId)
        {
            //Cache adminuserid, moduleid, buttonid
            var CurrentUser = _workContext.CurrentAdminUser;
            string cacheKey = string.Format(DefaultCacheConfig.AdminUserColumnPermissionButton, CurrentUser.Id, columnId, buttonId);
            return _cacheManager.Get(cacheKey, () => {
                var userRoles = CurrentUser.GetAdminRoles();
                if (userRoles.Any(r => r.IsAdmin == EntityEnumConfig.YesorNo.YES))
                    return true;
                bool permission = false;
                var RolePermissions = _columnService.GetAdminRoleColumnMappingsByRoleIds(userRoles.Select(r => r.Id).ToArray());
                foreach (var mp in RolePermissions.Where(m => m.FKColumnId == columnId).ToList())
                {
                    if (!string.IsNullOrEmpty(mp.AuthorizedButtonIds))
                    {
                        //DavidLee 2020/10/14修复按钮使用IndexOf判断是否有操作权限的错误
                        //if (mp.AuthorizedButtonIds.IndexOf(buttonId.ToString()) >= 0)
                        if (mp.AuthorizedButtonIds.Split(',').Any(authid => authid.Equals(buttonId.ToString())))
                        {
                            permission = true;
                            break;
                        }
                    }
                }
                return permission;
            });
        }

        public virtual IList<SystemDataDicButtonModel> GetSystemModuleAssignedButtons(string moduleUri)
        {
            var systemModule = _systemModuleService.GetSystemModuleByUri(moduleUri.ToLowerInvariant());
            if (systemModule == null || systemModule.ModuleAssignedButtons == null || systemModule.ModuleAssignedButtons.Count == 0) return null;
            string cacheKey = string.Format(DefaultCacheConfig.SystemModuleAssignedButtonsCacheKey, _workContext.CurrentAdminUser.Id, systemModule.Id);
            return _cacheManager.Get(cacheKey, () =>
            {
                var buttonIds = systemModule.ModuleAssignedButtons;
                var buttons = _systemdatadicService.GetSystemDataDicButtonsByIds(buttonIds.ToArray());
                IList<SystemDataDicButtonModel> btnModels = new List<SystemDataDicButtonModel>();
                foreach (var btn in buttons)
                    btnModels.Add(btn.ToModel<SystemDataDicButtonModel>());
                return btnModels;
            });
        }

        public virtual IList<SystemModuleModel> GetAuthorizedSystemModulesByAdminRoleId(AdminRole role)
        {
            var key = string.Format(DefaultCacheConfig.SystemModuleAuthorizedCacheKey, role.Id);
            return _cacheManager.Get(key, () =>
            {
                bool IsAdmin = role.IsAdmin == EntityEnumConfig.YesorNo.YES;
                var query = from sm in _systemmoduleRepository.Table
                            join rmp in _adminrolesysmoduleMappingRepo.Table on sm.Id equals rmp.FKSystemModuleId
                            where rmp.FKAdminRoleId == role.Id && sm.IsActive == EntityEnumConfig.YesorNo.YES
                            orderby sm.Id, sm.DisplayOrder
                            select new SystemModuleModel()
                            {
                                Id = sm.Id,
                                ParentId = sm.ParentId,
                                ParentIdDisplay = string.Empty,
                                ModuleType = sm.ModuleType,
                                ModuleName = sm.ModuleName,
                                DisplayName = sm.DisplayName,
                                IconCssClass = sm.IconCssClass,
                                UrlRoute = sm.UrlRoute,
                                //DavidLee 2019/1/14借用IsActive存储角色对应系统模块是否有使用(查看)权限
                                IsActive = IsAdmin ? EntityEnumConfig.YesorNo.YES : rmp.IsAllow,
                                DisplayOrder = sm.DisplayOrder,
                                OpenOnNewWindow = sm.OpenOnNewWindow,
                                ModuleAssignedButtons = null,
                                ModuleAssignedButtonsDic = null,
                                ModuleAuthorizedButtons = null,
                                //借用IconText存储角色对应模块有操作权限的操作按钮Id
                                //用户角色标识为Admin(超级管理员)，则只要系统模块为配了操作按钮，角色就有操作权限
                                IconText = IsAdmin ? sm.ModuleAssignedButtonIds : rmp.AuthorizedButtonIds,
                                //借用ModuleAssignedButtonNames存储系统模块所包含的操作按钮
                                ModuleAssignedButtonNames = sm.ModuleAssignedButtonIds
                            };
                var models = query.ToList();
                var buttons = _systemdatadicService.GetSystemDataDicButtonsAll();
                string buttonName = string.Empty;
                models.ForEach(m =>
                {
                    if (!string.IsNullOrEmpty(m.IconText))
                    {
                        m.ModuleAuthorizedButtons = m.IconText.Split(",").Select(Int32.Parse).ToList();
                        m.IconText = string.Empty;
                    }
                    if (!string.IsNullOrEmpty(m.ModuleAssignedButtonNames))
                    {
                        m.ModuleAssignedButtonsDic = m.ModuleAssignedButtonNames.Split(",").Select(Int32.Parse)
                                                      .ToDictionary(key => key, key => buttons.FirstOrDefault(b => b.Id == key).ButtonName);
                        m.ModuleAssignedButtonNames = string.Empty;
                        //m.ModuleAssignedButtons = m.ModuleAssignedButtonNames.Split(",").Select(Int32.Parse).ToList();

                        //m.ModuleAssignedButtons.ForEach(id =>
                        //{
                        //    buttonName = buttons.FirstOrDefault(b => b.Id == id).ButtonName;
                        //    m.ModuleAssignedButtonsDic.Add(id, buttonName);
                        //});
                    }
                });
                return models;
            });
        }

        public virtual IList<ColumnAthorizedModel> GetColumnAuthorizeData(AdminRole role, EntityEnumConfig.ColumnType columnType)
        {
            var key = string.Format(DefaultCacheConfig.ColumnAuthorizedCacheKey, role.Id, (int)columnType);
            return _cacheManager.Get(key, () =>
            {
                bool IsAdmin = role.IsAdmin == EntityEnumConfig.YesorNo.YES;
                var langService = EngineContext.Current.Resolve<ILanguageService>();
                var languages = langService.GetLanguagesAll();
                var query = from column in _columnRepository.Table
                            join rmp in _adminrolecolumnMappingRepository.Table on column.Id equals rmp.FKColumnId
                            where rmp.FKAdminRoleId == role.Id && column.ColumnType == columnType
                            orderby column.FKLanguageId, column.Level, column.DisplayOrder
                            select new ColumnAthorizedModel()
                            {
                                Id = column.Id,
                                Pid = column.PID,
                                DisplayOrder = column.DisplayOrder,
                                Title = column.DisplayName,
                                Language = column.FKLanguageId.ToString(), //languages.FirstOrDefault(l => l.Id == column.FKLanguageId)?.DisplayName ?? "未知",
                                AssignedButtonIds = rmp.AssignedButtonIds,
                                IsAllow = IsAdmin ? true : rmp.IsAllow == EntityEnumConfig.YesorNo.YES ? true : false,
                                AssignedButtons = null,
                                //用户角色标识为Admin(超级管理员)，则只要系统模块配了操作按钮，角色就有操作权限
                                AuthorizedButtonIds = IsAdmin ? rmp.AssignedButtonIds : rmp.AuthorizedButtonIds
                            };
                var models = query.ToList();
                var buttons = _systemdatadicService.GetSystemDataDicButtonsAll();
                string buttonName = string.Empty;
                models.ForEach(m =>
                {
                    if (!string.IsNullOrEmpty(m.AssignedButtonIds))
                    {
                        m.AssignedButtons = m.AssignedButtonIds.Split(",").Select(Int32.Parse)
                                               .ToDictionary(key => key, key => buttons.FirstOrDefault(b => b.Id == key)?.ButtonName ?? "未知");
                    }
                    m.Language = languages.FirstOrDefault(l => l.Id.ToString() == m.Language)?.DisplayName ?? "未知";
                });
                return models;
            });
        }
        #endregion
    }
}
